与前一章中简单的calc语言相比，真正的编程面临更多挑战。为了了解其中的细节，我们将在本章和后续章节中使用Modula-2的一小部分。Modula-2设计良好，可选支持泛型和面向对象编程(OOP)。但在本书中，我们并不打算创建一个完整的Modula-2编译器，所以我们将这个子集称为tinylang。

让我们从一个例子开始，看看tinylang编写的程序是什么样的。下面的函数使用欧几里得算法计算最大公约数:

\begin{shell}
MODULE Gcd;

PROCEDURE GCD(a, b: INTEGER) : INTEGER;
VAR t: INTEGER;
BEGIN
    IF b = 0 THEN
        RETURN a;
    END;
    WHILE b # 0 DO
        t := a MOD b;
        a := b;
        b := t;
    END;
    RETURN a;
END GCD;

END Gcd.
\end{shell}

现在我们对使用这种语言编写的程序有了大致的了解，下面来快速了解一下tinylang子集的语法。在接下来的几节中，我们将使用这种语法来派生词法分析器和解析器:

\begin{shell}
compilationUnit
    : "MODULE" identifier ";" ( import )* block identifier "." ;
import : ( "FROM" identifier )? "IMPORT" identList ";" ;
block
    : ( declaration )* ( "BEGIN" statementSequence )? "END" ;
\end{shell}

Modula-2中的编译单元以MODULE关键字开头，后面跟着模块的名称。模块的内容可以包含一个导入模块的列表、声明，以及一个包含在初始化时运行语句的块:

\begin{shell}
declaration
    : "CONST" ( constantDeclaration ";" )*
    | "VAR" ( variableDeclaration ";" )*
    | procedureDeclaration ";" ;
\end{shell}

声明引入了常量、变量和过程。常量的声明以CONST关键字作为前缀。类似地，变量声明以VAR关键字开头。常量的声明非常简单:

\begin{shell}
constantDeclaration : identifier "=" expression ;
\end{shell}

标识符的名称不变。该值是从一个表达式派生的，该表达式必须在编译时可计算。变量的声明会有点复杂:

\begin{shell}
variableDeclaration : identList ":" qualident ;
qualident : identifier ( "." identifier )* ;
identList : identifier ( "," identifier)* ;
\end{shell}

为了能够一次声明多个变量，需要使用标识符列表。类型名可能来自另一个模块，本例中以模块名作为前缀，这称为限定标识符(qualified identifier):

\begin{shell}
procedureDeclaration
    : "PROCEDURE" identifier ( formalParameters )? ";"
        block identifier ;
formalParameters
    : "(" ( formalParameterList )? ")" ( ":" qualident )? ;
formalParameterList
    : formalParameter (";" formalParameter )* ;
formalParameter : ( "VAR" )? identList ":" qualident ;
\end{shell}

前面的代码展示了如何常量、变量声明和过程。过程可以有参数和返回类型，普通参数以值的形式传递，VAR参数以引用的形式传递。块规则中缺少的另一部分是statementSequence，是一个单条语句的列表:

\begin{shell}
statementSequence
    : statement ( ";" statement )* ;
\end{shell}

若语句后面跟着另一个语句，则用分号分隔，所以只支持Modula-2语句的子集:

\begin{shell}
statement
    : qualident ( ":=" expression | ( "(" ( expList )? ")" )? )
    | ifStatement | whileStatement | "RETURN" ( expression )? ;
\end{shell}

该规则的第一部分描述了一个赋值或过程调用。限定标识符后面跟:=是赋值操作。若后面跟着(，则是一个过程调用。其他语句是常见的控制语句:

\begin{shell}
ifStatement
    : "IF" expression "THEN" statementSequence
        ( "ELSE" statementSequence )? "END" ;
\end{shell}

IF语句也有简化的语法，只能有一个ELSE块。使用该语句，可以有条件地保护语句:

\begin{shell}
whileStatement
    : "WHILE" expression "DO" statementSequence "END" ;
\end{shell}

WHILE语句描述了一个由条件保护的循环。与IF语句一起，能够用tinylang编写简单的算法。最后，还缺少了表达式的定义:

\begin{shell}
expList
    : expression ( "," expression )* ;
expression
    : simpleExpression ( relation simpleExpression )? ;
relation
    : "=" | "#" | "<" | "<=" | ">" | ">=" ;
simpleExpression
    : ( "+" | "-" )? term ( addOperator term )* ;
addOperator
    : "+" | "-" | "OR" ;
term
    : factor ( mulOperator factor )* ;
mulOperator
    : "*" | "/" | "DIV" | "MOD" | "AND" ;
factor
    : integer_literal | "(" expression ")" | "NOT" factor
    | qualident ( "(" ( expList )? ")" )? ;
\end{shell}

表达式语法与前一章中的calc非常相似，只支持INTEGER和BOOLEAN数据类型。

此外，还使用了标识符和integer\_literal标记。标识符是以字母或下划线开头，后面跟着字母、数字和下划线的名称。整数字面值是一个十进制数字序列或后跟字母H的十六进制数字序列。

这些规则已经有很多了，我们只介绍Modula-2的一部分!不过，也可以在这个子集中编写小型应用程序。来实现一个tinylang编译器吧!
